/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtMtTable.h"
#include "WtTable.h"
#include "WtUtil.h"

/* Size of the table */

int WtMtTableSize(Tcl_Obj *mtTbl)
{
  int size;
  WtMtTableLock(mtTbl);
  size = WtTableSize(WtMtTableStorage(mtTbl));
  WtMtTableUnlock(mtTbl);
  return size;
}

/* Check if the key exists */

int WtMtTableHas(Tcl_Obj *mtTbl, Tcl_Obj *key)
{
  int ret;
  WtMtTableLock(mtTbl);
  ret = WtTableHas(WtMtTableStorage(mtTbl), key);
  WtMtTableUnlock(mtTbl);
  return ret;
}

/* Get an entry */

Tcl_Obj *WtMtTableGet(Tcl_Obj *mtTbl, Tcl_Obj *key)
{
  Tcl_Obj *val;
  WtMtTableLock(mtTbl);
  val = WtTableGet(WtMtTableStorage(mtTbl), key);
  Tcl_IncrRefCount(val);
  WtMtTableUnlock(mtTbl);
  return val;
}

/* Set a table entry */

void WtMtTableSet(Tcl_Obj *mtTbl, Tcl_Obj *key, Tcl_Obj *val)
{
  WtMtTableLock(mtTbl);

  key = Tcl_DuplicateObj(key);
  Tcl_IncrRefCount(key);

  val = Tcl_DuplicateObj(val);
  Tcl_IncrRefCount(val);

  WtTableSet(WtMtTableStorage(mtTbl), key, val);

  Tcl_DecrRefCount(key);
  Tcl_DecrRefCount(val);

  WtMtTableUnlock(mtTbl);
}

/* Remove an entry */

void WtMtTableRemove(Tcl_Obj *mtTbl, Tcl_Obj *key)
{
  WtMtTableLock(mtTbl);
  WtTableRemove(WtMtTableStorage(mtTbl), key);
  WtMtTableUnlock(mtTbl);
}

/* Remove a list */

int WtMtTableRemoveList(Tcl_Obj *mtTbl, Tcl_Obj *list,
    Tcl_Interp *interp)
{
  int ret;
  WtMtTableLock(mtTbl);
  ret = WtTableRemoveList(WtMtTableStorage(mtTbl), list, interp);
  WtMtTableUnlock(mtTbl);
  return ret;
}

/* Get keys */

int WtMtTableKeys(Tcl_Obj *mtTbl, Tcl_Obj **keys, Tcl_Interp *interp)
{
  int ret;
  WtMtTableLock(mtTbl);
  ret = WtTableKeys(WtMtTableStorage(mtTbl), keys, interp);
  WtMtTableUnlock(mtTbl);
  return ret;
}

/* Get values */

int WtMtTableValues(Tcl_Obj *mtTbl, Tcl_Obj **vals, Tcl_Interp *interp)
{
  int ret;
  WtMtTableLock(mtTbl);
  ret = WtTableKeys(WtMtTableStorage(mtTbl), vals, interp);
  WtMtTableUnlock(mtTbl);
  return ret;
}

/* Update table */

int WtMtUpdateTable(Tcl_Obj *dest, Tcl_Obj *src)
{
  int ret;

  WtMtTableLock(dest);
  if (dest != src) {
    WtMtTableLock(src);
  }

  ret = WtUpdateTable(WtMtTableStorage(dest),
    WtMtTableStorage(src));

  WtMtTableUnlock(dest);
  if (dest != src) {
    WtMtTableUnlock(src);
  }

  return ret;
}

void WtMtTableClear(Tcl_Obj *mtTbl)
{
  WtMtTableLock(mtTbl);
  WtClearTable(WtMtTableStorage(mtTbl));
  WtMtTableUnlock(mtTbl);
}

void WtMtTableLock(Tcl_Obj *mtTbl)
{
  Tcl_MutexLock(WtMtTableRep(mtTbl)->lock);
}

void WtMtTableUnlock(Tcl_Obj *mtTbl)
{
  Tcl_MutexUnlock(WtMtTableRep(mtTbl)->lock);
}

WtMtTable *WtMtTableRep(Tcl_Obj *mtTbl)
{
  return (WtMtTable *)(mtTbl->internalRep.twoPtrValue.ptr1);
}

Tcl_Obj *WtMtTableStorage(Tcl_Obj *mtTbl)
{
  return WtMtTableRep(mtTbl)->tbl;
}

/* Standard table object */

Tcl_Obj *WtNewMtTableObj()
{
  Tcl_Obj *obj = Tcl_NewObj();
  WtMtTable *mtt = (WtMtTable *)ckalloc(sizeof(WtMtTable));
  mtt->tbl = WtNewTableObj();
  Tcl_IncrRefCount(mtt->tbl);
  mtt->lock = NULL;
  obj->internalRep.twoPtrValue.ptr1 = mtt;
  obj->internalRep.twoPtrValue.ptr2 = NULL;
  Tcl_InvalidateStringRep(obj);
  return obj;
}

struct Tcl_ObjType WtMtTableType =
{
  "mtTable",
  WtFreeMtTable,
  WtDupMtTable,
  WtUpdateMtTableString,
  WtSetMtTableFromAny
};

void WtFreeMtTable(Tcl_Obj *mtTbl)
{
  WtMtTable *mtt = WtMtTableRep(mtTbl);
  Tcl_MutexLock(mtt->lock);
  if (mtt->tbl) {
    Tcl_DecrRefCount(mtt->tbl);
  }
  Tcl_MutexUnlock(mtt->lock);
  Tcl_MutexFinalize(mtt->lock);
  ckfree((char *)mtt);
}

void WtDupMtTable(Tcl_Obj *src, Tcl_Obj *dest)
{
  WtMtTableLock(src);
  WtDupTable(src, dest);
  WtMtTableUnlock(src);
}

void WtUpdateMtTableString(Tcl_Obj *tbl)
{
  WtMtTableLock(tbl);
  WtUpdateProtectedObjString(tbl);
  WtMtTableUnlock(tbl);
}

int WtSetMtTableFromAny(Tcl_Interp *interp, Tcl_Obj *obj)
{
  return WtSetProtectedObjFromAny(interp, obj, &WtMtTableType);
}
